#pragma once

#include <iostream>
#include <unordered_map>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "Connection.hpp"
#include "Epoller.hpp"
#include "Comm.hpp"
#include "Log.hpp"

#define EPOLL_NOBLOCK  -1

class TcpServer
{
    const static int gmaxevents = 64;

public:
    TcpServer()
        : _isrunning(false), _timeout(EPOLL_NOBLOCK)
    {
        _epoller.InitEpoller();
    }

    // 判断连接是否存在
    bool IsConnectionExists(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

    // 修改连接文件描述符是否关系读写时间
    void EnableReadWrite(Connection *conn, bool readable, bool writeable)
    {
        if (IsConnectionExists(conn->GetSockfd()))
            return;

        uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0);

        conn->SetEvents(events);
    }

    // 创建新连接，并加入到unordered_map中
    void AddConnection(Connection *conn)
    {
        if (IsConnectionExists(conn->GetSockfd()))
            return;

        lg.LogMessage(Info, "get a new connection , sockfd : %d \n", conn->GetSockfd());
        _connections[conn->GetSockfd()] = conn;
        _epoller.AddEvent(conn->GetSockfd(), conn->GetEvents());

        SetNonBlock(conn->GetSockfd()); // 将文件描述符设置为非阻塞
        lg.LogMessage(Info,"set %d non block\n",conn->GetSockfd());
    }

    // 从unordered_ma中删除连接
    void DelConnection(int sockfd)
    {
        if(!IsConnectionExists(sockfd))
            return;
        // 从unordered_map中删除该连接
        _connections.erase(sockfd);
        // 从Epoller中删除对应文件描述符
        _epoller.DelEvents(sockfd);
    }

    // 单次循环
    void LoopOnce(int timeout)
    {
        int n = _epoller.Wait(_revs, gmaxevents, timeout);
        for (int i = 0; i < n; i++)
        {
            // 将所有的异常，转换为读写异常
            if (_revs[i].events & EPOLLERR || _revs[i].events & EPOLLHUP)
            {
                _revs[i].events |= (EPOLLIN | EPOLLOUT);
            }

            int sockfd = _revs[i].data.fd;
            if (_revs[i].events & EPOLLIN)
            {
                if (_connections[sockfd]->_reader)
                    _connections[sockfd]->_reader(_connections[sockfd]);
            }

            if (_revs[i].events & EPOLLOUT)
            {
                if (_connections[sockfd]->_writer)
                    _connections[sockfd]->_writer(_connections[sockfd]);
            }
        }
    }

    void Dispatcher() // 事件派发器
    {
        _isrunning = true;
        while (_isrunning)
        {
            _epoller.DebugFdList(); // for test
            LoopOnce(_timeout);
        }
    }

    ~TcpServer()
    {
    }

private:
    unordered_map<int, Connection *> _connections;
    Epoller _epoller;

    struct epoll_event _revs[gmaxevents];
    int _timeout;

    bool _isrunning;
};
